package com.superbytecode.cloud.rocketmq.producer;

import com.superbytecode.cloud.common.context.SpringContextUtil;
import com.superbytecode.cloud.common.model.mq.MQConfigure;
import com.superbytecode.cloud.common.model.mq.Topic;
import com.superbytecode.cloud.rocketmq.configure.MQConfigurationProperties;
import com.superbytecode.cloud.rocketmq.configure.MQProviderEnum;
import com.superbytecode.cloud.rocketmq.exception.MQBizException;
import com.superbytecode.cloud.rocketmq.producer.provider.AliMQProduceProvider;
import com.superbytecode.cloud.rocketmq.producer.provider.OpenMQProduceProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;

import java.lang.reflect.*;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * All rights Reserved, Designed By www.super-master.com
 *
 * @ProjectName: beacon-cloud
 * @Package: com.superbytecode.cloud.rocketmq.producer
 * @ClassName: RocketMQTemplateInvocationHandler
 * @Description: []
 * <p> </p>
 * @Author: [Lance Ting]
 * @Date: 2024/11/1 15:57
 * @Version: V1.0
 * @Copyright: 2024 www.super-master.com Inc. All rights reserved.
 * TODO: 注意,本文件Lance Ting所作,如果转载或使用请标明具体出处!
 **/
@Slf4j
public class MQTemplateInvocationHandler extends RocketMQTemplate implements InvocationHandler {
    /**
     * 缓存topic对应的注解
     */
    private static final Map<Class<?>, Topic> MESSAGE_QUEUE_MAP = new ConcurrentHashMap<>();
    /**
     * 策略
     */
    private MQProduceProvider mqProduceProvider;

    /**
     * mq代理方法调用
     *
     * @param proxy  调用该方法的代理实例
     * @param method 方法
     * @param args   参数 [configure(可以为空),body]
     * @throws Throwable 异常
     * @see UndeclaredThrowableException
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Objects.equals(method.getDeclaringClass(), Object.class)) {
            return method.invoke(this, args);
        }
        // 获取代理参数：[configure,body]或者[body]
        if (args.length < 1 || args.length > 2) {
            throw new MQBizException("args length must be 1 or 2");
        }
        MQConfigure configure = args.length > 1 ? (MQConfigure) args[0] : null;
        Object body = args.length > 1 ? args[1] : args[0];
        // 获取消息执行策略
        MQProduceProvider produceStrategy = obtainMQProduceStrategy();
        // 发送消息
        Topic topic = obtainTopic(proxy);
        String topicValue = SpringContextUtil.getSpringValue(topic.value());
        produceStrategy.producer(topicValue, configure, body);
        return null;
    }

    /**
     * 获取代理对象
     *
     * @param type 类型
     * @return 代理对象
     */
    public static Object newProxyInstance(Class<?> type) {
        return Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new MQTemplateInvocationHandler());
    }

    private MQProduceProvider obtainMQProduceStrategy() {
        if (null != mqProduceProvider) {
            return mqProduceProvider;
        }
        MQConfigurationProperties properties = SpringContextUtil.getBean(MQConfigurationProperties.class);
        MQProviderEnum rocketMQProvider = properties.getProvider();
        switch (rocketMQProvider) {
            case OPEN:
                mqProduceProvider = SpringContextUtil.getBean(OpenMQProduceProvider.class);
                break;
            case ALI:
                mqProduceProvider = SpringContextUtil.getBean(AliMQProduceProvider.class);
                break;
            default:
                throw new MQBizException("Not exist rocketMQ provider for {" + rocketMQProvider + "}");
        }
        return mqProduceProvider;
    }

    private Topic obtainTopic(Object proxy) {
        Topic messageQueue;
        Class<?> proxyClass = proxy.getClass();
        // 缓存取值，命中则直接返回
        if (Objects.nonNull(messageQueue = MESSAGE_QUEUE_MAP.get(proxyClass))) {
            return messageQueue;
        }
        // 未命中
        Type[] interfaceTypes = proxyClass.getGenericInterfaces();
        Class<?> genericInterface = (Class<?>) interfaceTypes[0];
        Topic topic = genericInterface.getAnnotation(Topic.class);
        MESSAGE_QUEUE_MAP.put(proxyClass, topic);
        return topic;
    }
}